﻿CsvSegítő ~  A .NET library for reading and writing CSV files. Extremely fast, flexible and easy to use. Supports reading and writing of custom class objects.
==============================================================================================================================================================
## Current Status
CsvSegítő is currently on v1.0. 

## Requirements
CsvSegítő has no external direct dependencies.

## Usage 
### Reading :
Reading is setup to be as simple as possible. If you have a class structure setup that mirrors the CSV file, you can read the whole file into an enumerable.

var csv = new CsvReader( textReader );
var records = csv.GetRecords<MyClass>();
If you want to customize how the CSV file maps to your custom class objects, you can use mapping.

The IEnumerable<T> that is returned will yield results. This means that a result isn't returned until you actually access it. This is handy because the whole file won't be loaded into memory, and the file will be read as you access each row. If you do something like Count() on the IEnumerable<T>, the whole file needs to be read and you won't be able to iterate over it again without starting over. If you need to iterate the records more than once (like using Count), you can load everything into a list and the work on the data.
```csharp
var csv = new CsvReader( textReader );
var records = csv.GetRecords<MyClass>().ToList();
Reading records manually
You can loop the rows and read them manually.

var csv = new CsvReader( textReader );
while( csv.Read() )
{
    var record = csv.GetRecord<MyClass>();
}
```

Reading individual fields
You can also read each field manually if you like.
```csharp
var csv = new CsvReader( textReader );
while( csv.Read() )
{
    var intField = csv.GetField<int>( 0 );
    var stringField = csv.GetField<string>( 1 );
    var boolField = csv.GetField<bool>( "HeaderName" );
}
```

TryGetField
If you might have inconsistencies with getting fields, you can use TryGetField.

```csharp
var csv = new CsvReader( textReader );
while( csv.Read() )
{
    int intField;
    if( !csv.TryGetField( 0, out intField ) )
    {
        // Do something when it can't convert.
    }
}
```
### Parsing: 
You can also use the parser directly without using the reader. The parser will give back an array of strings for each row that is read, and null when it is finished.

```csharp
var parser = new CsvParser( textReader );
while( true )
{
    var row = parser.Read();
    if( row == null )
    {
        break;
    }
}
```
### Writing: 
#### Writing all records
Writing is setup to be as simple as possible. If you have a class structure setup that mirrors the CSV file, you can write the whole file from an enumerable.

```csharp
var csv = new CsvWriter( textWriter );
csv.WriteRecords( records );
Writing records manually
You can loop the objects and write them manually.

var csv = new CsvWriter( textWriter );
foreach( var item in list )
{
    csv.WriteRecord( item );
}
```
#### Writing individual fields
You can also write each field manually if you like.

```csharp
var csv = new CsvWriter( textWriter );
foreach( var item in list )
{
    csv.WriteField( "a" );
    csv.WriteField( 2 );
    csv.WriteField( true );
    csv.NextRecord();
}
```
### Mapping:
#### Auto Mapping
If you don't supply a mapping file, auto mapping will be used. Auto mapping will map the properties in your class in the order they appear in. If the property is a custom class, it recursively maps the properties from that class in the order they appear in. If the auto mapper hits a circular reference, it will stop going down that reference branch.

#### Fluent Class Mapping
If your CSV file doesn't match up exactly with your custom class, you can use a fluent class map to set options for how the class maps to the file. You need to register your class map in configuration.

```csharp
public sealed class MyClassMap : CsvClassMap<MyClass>
{
    public MyClassMap()
    {
        Map( m => m.Id );
        Map( m = > m.Name );
    }
}
```

#### Reference Map

Reference maps are used to map a property that is a custom class to it's own mapping that maps those properties to several CSV columns. You can nest reference maps as many layers deep as you like.

```csharp
public sealed class PersonMap : CsvClassMap<Person>
{
    public PersonMap()
    {
        Map( m => m.Id );
        Map( m => m.Name );
        References<AddressMap>( m => m.Address );
    }
}

public sealed class AddressMap : CsvClassMap<Address>
{
    public AddressMap()
    {
        Map( m => m.Street );
        Map( m => m.City );
        Map( m => m.State );
        Map( m => m.Zip );
    }
}
```
#### Index
When mapping by index you specify the index of the CSV column that that you want to use for that property.

```csharp
public sealed class MyClassMap : CsvClassMap<MyClass>
{
    public MyClassMap()
    {
        Map( m => m.Id ).Index( 0 );
        Map( m => m.Name ).Index( 1 );
    }
}
```
#### Name

When mapping by name you specify the name of the CSV column that you want to use for that property. For this to work, the CSV file must have a header record. The name you specify must match with the name of the header record.

```csharp
public sealed class MyClassMap : CsvClassMap<MyClass>
{
    public MyClassMap()
    {
        Map( m => m.Id ).Name( "The Id Column" );
        Map( m => m.Name ).Name( "The Name Column" );
    }
}
```
#### Name Index

Sometimes CSV files have multiple columns with the same name. When this happens, you can use NameIndex to specify which column name you are referring to. The NameIndex is NOT the column in the CSV file.

```csharp
public sealed class MyClassMap : CsvClassMap<MyClass>
{
    public MyClassMap()
    {
        Map( m => m.FirstName ).Name( "Name" ).NameIndex( 0 );
        Map( m => m.LastName ).Name( "Name" ).NameIndex( 1 );
    }
}
```
#### Default

Default is used to set a default value you want to use if the field is empty.

```csharp
public sealed class MyClassMap : CsvClassMap<MyClass>
{
    public override void MyClassMap()
    {
        Map( m => m.Id ).Index( 0 ).Default( -1 );
        Map( m => m.Name ).Index( 1 ).Default( "Unknown" );
    }
}
```

#### Type Converter

If the value of the CSV field can't automatically be converted into the type of the property, you can specify a custom CsvSegítő.TypeConversion.ITypeConverter to be used to convert the value. See Type Conversion for documentation on how to create a custom type converter.

```csharp
public sealed class MyClassMap : CsvClassMap<MyClass>
{
    public MyClassMap()
    {
        Map( m => m.Id ).Index( 0 ).TypeConverter<MyIdConverter>();
    }
}
```

##### Type Converter Options

The default built in converters will handle most cases of type conversion, but sometimes there are some small changes that you'd like to make, but don't want to create a whole new type converter that just parses an int (for example) differently. You can specify some type converter options to handle these cases.

```csharp
public sealed class MyClassMap : CsvClassMap<MyClass>
{
    public MyClassMap()
    {
        Map( m => m.Description ).Index( 0 ).TypeConverterOption( CultureInfo.InvariantCulture );
        Map( m => m.TimeStamp ).Index( 1 ).TypeConverterOption( DateTimeStyles.AdjustToUniversal );
        Map( m => m.Cost ).Index( 2 ).TypeConverterOption( NumberStyles.Currency );
        Map( m => m.CurrencyFormat ).Index( 3 ).TypeConverterOption( "C" );
        Map( m => m.BooleanValue ).Index( 4 ).TypeConverterOption( true, "sure" ).TypeConverterOption( false, "nope" );
    }
}
```
##### Convert Using

When all else fails, you can use ConvertUsing. ConvertUsing allows you to write custom code inline to convert the row into a single property value.

```csharp
public sealed class MyClassMap : CsvClassMap<MyClass>
{
    public MyClassMap()
    {
        // Constant value.
        Map( m => m.Constant ).ConvertUsing( row => 3 );
        // Aggregate of two rows.
        Map( m => m.Aggregate ).ConvertUsing( row => row.Get<int>( 0 ) + row.Get<int>( 1 ) );
        // Collection with a single value.
        Map( m => m.Names ).ConvertUsing( row => new List<string>{ row.Get<string>( "Name" ) } );
        // Just about anything.
        Map( m => m.Anything ).ConvertUsing( row =>
        {
            // You can do anything you want in a block.
            // Just make sure to return the same type as the property.
        } );
    }
}
```
#### Runtime Mapping

Maps can be created at runtime. In fact the auto map feature does everything dynamically. 

Another simple example is shown below:

```csharp
var customerMap = new DefaultCsvClassMap();

// mapping holds the Property - csv column mapping 
foreach( string key in mapping.Keys )
{
    var columnName = mapping[key].ToString();

    if( !String.IsNullOrEmpty( columnName ) )
    {
        var propertyInfo = typeof( Customer ).GetType().GetProperty( key );
        var newMap = new CsvPropertyMap( propertyInfo );
        newMap.Name( columnName );
        customerMap.PropertyMaps.Add( newMap );
    }
}

csv.Configuration.RegisterClassMap(CustomerMap);
```
### Configuration: 
#### Allow Comments
This flag tells the parser whether comments are enabled.

```csharp
// Default value
csv.Configuration.AllowComments = false;
```

#### Auto Map
This is used to generate a CsvClassMap from a type automatically without a fluent class mapping. This will try to map all properties including creating reference maps for properties that aren't native types. If the auto mapper detects a circular reference, it will not continue down that path.

```csharp
var generatedMap = csv.Configuration.AutoMap<MyClass>();
```

#### Buffer Size
The size of the internal buffer that is used when reader or writing data to and from the TextReader and TextWriter. Depending on where your TextReader or TextWriter came from, you may want to make this value larger or smaller.
```csharp
// Default value
csv.Configuration.BufferSize = 2048;
```
#### Comment
The value used to denote a line that is commented out.
```csharp
// Default value
csv.Configuration.Comment = '#';
```
#### Count Bytes
A flag that will tell the parser to keep a count of all the bytes that have been read. You need to set Configuration.Encoding to the same encoding of the CSV file for this to work properly. This will also slow down parsing of the file.
```csharp
// Default value
csv.Configuration.CountBytes = false;
```
#### Culture Info
The culture info used to read and write. This can be overridden per property in the mapping configuration.
```csharp
// Default value
csv.Configuration.CultureInfo = CultureInfo.CurrentCulture;
```
#### Delimiter
The value used to separate the fields in a CSV row.
```csharp
// Default value
csv.Configuration.Delimiter = ",";
```
#### Detect Column Count Changes
This flag will check for changes in the number of column from row to row. If true and a change is detected, a CsvBadDataException will be thrown.
```csharp
// Default value
csv.Configuration.DetectColumnCountChanges = false;
```
#### Encoding
The encoding of the CSV file. This is only used when counting bytes. The underlying TextReader and TextWriter will have it's own encoding that is used.
```csharp
// Default value
csv.Configuration.Encoding = Encoding.UTF8;
```
#### Has Header Record
This flag tells the reader/writer if there is a header row in the CSV file. The must be true for mapping properties by name to work (and there must be a header row).
```csharp
// Default value
csv.Configuration.HasHeaderRecord = true;
```
#### Ignore Header White Space
This flag tells the reader to ignore white space in the headers when matching the columns to the properties by name.
```csharp
// Default value
csv.Configuration.IgnoreHeaderWhiteSpace = false;
```
#### Ignore Private Accessor
A flag that tells the reader and writer to ignore private accessors when reading and writing. By default you can't read from a private getter or write to a private setter. Turn this on will allow that. Properties that can't be read from or written to are silently ignored.
```csharp
// Default value
csv.Configuration.IgnorePrivateAccessor = false;
```
#### Ignore Reading Exceptions
A flag that tells the reader to swallow any exceptions that occur while reading and to continue on. Exceptions that occur in the parser will not be ignored. Parser exceptions mean the file is bad in some way, and the parser isn't able to recover.
```csharp
// Default value
csv.Configuration.IgnoreReadingExceptions = false;
```
#### Ignore Quotes
A flag that tells the parser to ignore quotes as an escape character and treat it like any other character.
```csharp
// Default value
csv.Configuration.IgnoreQuotes = false;
```
#### Is Header Case Sensitive
This flag sets whether matching CSV header names will be case sensitive.
```csharp
// Default value
csv.Configuration.IsHeaderCaseSensitive = true;
```

#### Maps
You are able to access the registered class maps.
```csharp
var myMap = csv.Configuration.Maps[typeof( MyClass )];
```csharp
#### Property Binding Flags
PropertyBindingFlags are the flags used to find the properties on the custom class.
```csharp
// Default value
csv.Configuration.PropertyBindingFlags = BindingFlags.Public | BindingFlags.Instance;
```
#### Quote
The value used to escape fields that contain a delimiter, quote, or line ending.
```csharp
// Default value
csv.Configuration.Quote = '"';
```
#### Quote All Fields
A flag that tells the writer whether all fields written should have quotes around them; regardless if the field contains anything that should be escaped. Both QuoteAllFields and QuoteNotFields cannot be true at the same time. Setting one to true will set the other to false.
```csharp
// Default value
csv.Configuration.QuoteAllFields = false;
```
#### Quote No Fields
A flag that tell the writer whether all fields written should not have quotes around them; regardless if the field contains anything that should be escaped. Both QuoteAllFields and QuoteNotFields cannot be true at the same time. Setting one to true will set the other to false.
```csharp
// Default value
csv.Configuration.QuoteAllFields = false;
```
#### Reading Exception Callback
If you have Configuration.IgnoreReaderExceptions on and you want to know that the exceptions have occurred and possibly do something with them, you can use this.
```csharp
csv.Configuration.ReadingExceptionCallback = ( ex, row ) =>
{
    // Log the exception and current row information.
};
```
#### Register Class Map
When using fluent class mapping, you need to register class maps for them to be used. You can register multiple class maps to be used.
```csharp
csv.Configuration.RegisterClassMap<MyClassMap>();
csv.Configuration.RegisterClassMap<AnotherClassMap>();
```
#### Skip Empty Records
A flag to let the reader know if a record should be skipped when reading if it's empty. A record is considered empty if all fields are empty.
```csharp
// Default value
csv.Configuration.SkipEmptyRecords = false;
```
#### Trim Fields
This flag tells the reader to trim whitespace from the beginning and ending of the field value when reading.
```csharp
// Default value
csv.Configuration.TrimFields = false;
```
#### Trim Headers
This flag tells the reader to ignore white space from the beginning and ending of the headers when matching the columns to the properties by name.
```csharp
// Default value
csv.Configuration.TrimHeaders = false;
```
#### Unregister Class Map
You can unregister a class map if needed.
```csharp
// Unregister single map.
csv.Configuration.UnregisterClassMap<MyClassMap>(); 

// Unregister all class maps. 
csv.Configuration.UnregisterClassMap();
```
#### Will Throw On Missing Field
This flag indicates if an exception should be thrown if reading and an expected field is missing. This is useful if you want to know if there is an issue with the CSV file.
```csharp
// Default value
csv.Configuration.WillThrowOnMissingField = true;
```
### Type Conversion
Type converters are the way CsvSegítő converts strings into .NET types, and .NET types into strings. The CsvSegítő type converter ecosystem stays close to the .NET

### Miscellaneous
#### Culture Specifics
Delimiter
Even though a culture has a specific list separator denoted by CultureInfo.TextInfo.ListSeparator, CSV files use a , as a separator per RFC 4180. If you have a file that uses a different delimiter, you can change the Configuration.Delimiter.

FAQ
I got an exception. How do I tell what line the exception was on?

There is a lot of information held in Exception.Data["CsvSegítő"].

ex:

Row: '3' (1 based)
Type: 'CsvSegítő.Tests.CsvReaderTests+TestBoolean'
Field Index: '0' (0 based)
Field Name: 'BoolColumn'
Field Value: 'two'

How can I use a DataReader or DataTable with CsvSegítő?

Writing to a CSV using a DataReader:
```csharp
var hasHeaderBeenWritten = false;
while( dataReader.Read() )
{
    if( !hasHeaderBeenWritten )
    {
        for( var i = 0; i < dataReader.FieldCount; i++ )
        {
            csv.WriteField( dataReader.GetName( i ) );
        }
        csv.NextRecord();
        hasHeaderBeenWritten = true;
    }

    for( var i = 0; i < dataReader.FieldCount; i++ )
    {
        csv.WriteField( dataReader[i] );
    }
    csv.NextRecord();
}
```

Writing to a CSV using a DataTable:
```csharp
using( var dt = new DataTable() )
{
    dt.Load( dataReader );
    foreach( DataColumn column in dt.Columns )
    {
        csv.WriteField( column.ColumnName );
    }
    csv.NextRecord();

    foreach( DataRow row in dt.Rows )
    {
        for( var i = 0; i < dt.Columns.Count; i++ )
        {
            csv.WriteField( row[i] );
        }
        csv.NextRecord();
    }
}
```
Reading from a CSV into a DataTable:
```csharp

while( csv.Read() )
{
    var row = dt.NewRow();
    foreach( DataColumn column in dt.Columns )
    {
        row[column.ColumnName] = csv.GetField( column.DataType, column.ColumnName );
    }
    dt.Rows.Add( row );
}

```